home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlutil.zip / MV_DIR.C < prev    next >
C/C++ Source or Header  |  1990-05-31  |  6KB  |  208 lines

  1. /* mv_dir -- rename directory
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Helper program for GNU mv on machines that lack the rename system call.
  19.  
  20.    Usage: mv_dir from to
  21.  
  22.    FROM must be an existing directory.  TO must not exist.
  23.  
  24.    Must be setuid root.
  25.  
  26.    Ian Dall (ian@sibyl.eleceng.ua.oz.au)
  27.    and David MacKenzie (djm@ai.mit.edu) */
  28.  
  29. #include <stdio.h>
  30. #include <errno.h>
  31. #include <sys/types.h>
  32. #include <signal.h>
  33. #include "system.h"
  34.  
  35. #ifdef STDC_HEADERS
  36. #include <stdlib.h>
  37. #include <errno.h>
  38. #else
  39. char *malloc ();
  40.  
  41. extern int errno;
  42. #endif
  43.  
  44. #ifndef HIPRI
  45. #define HIPRI -10
  46. #endif
  47.  
  48. #ifdef DEBUG
  49. #define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
  50. #define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
  51. #endif
  52.  
  53. /* The name this program was run with. */
  54. char *program_name;
  55.  
  56. char *basename ();
  57. char *xmalloc ();
  58. void error ();
  59. void strip_trailing_slashes ();
  60.  
  61. /* Return the name of the directory containing PATH. */
  62.  
  63. char *
  64. parent_dir (path)
  65.      char *path;
  66. {
  67.   char *dir;
  68.   char *base;
  69.   int length;
  70.  
  71.   base = rindex (path, '/');
  72.   if (base == NULL)
  73.     return ".";
  74.  
  75.   if (base > path)
  76.     base--;
  77.   length = base - path + 1;
  78.   dir = xmalloc (length + 1);
  79.   strncpy (dir, path, length);
  80.   dir[length] = '\0';
  81.   return dir;
  82. }
  83.  
  84. void
  85. main (argc, argv)
  86.      int argc;
  87.      char **argv;
  88. {
  89.   char *from, *to, *from_base, *from_parent, *to_parent;
  90.   struct stat from_stats, to_stats;
  91.   char *next_slash, temp;
  92.   int i;
  93.  
  94.   program_name = argv[0];
  95.   if (argc != 3)
  96.     {
  97.       fprintf (stderr, "Usage: %s existing-dir new-dir\n", program_name);
  98.       exit (2);
  99.     }
  100.   from = argv[1];
  101.   to = argv[2];
  102.   strip_trailing_slashes (from);
  103.   strip_trailing_slashes (to);
  104.   from_parent = parent_dir (from);
  105.   to_parent = parent_dir (to);
  106.  
  107.   /* Make sure `from' is not "." or "..". */
  108.   from_base = basename (from);
  109.   if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
  110.     error (1, 0, "cannot rename `.' or `..'");
  111.   
  112.   /* Even with an effective uid of root, link fails if the target exists.
  113.      That is what we want, so don't unlink `to' first.
  114.      However, we do need to check that the directories that link and unlink
  115.      will modify are writable by the user. */
  116.  
  117.   if (stat (from, &from_stats))
  118.     error (1, errno, "%s", from);
  119.   if ((from_stats.st_mode & S_IFMT) != S_IFDIR)
  120.     error (1, 0, "`%s' is not a directory", from);
  121.   if (access (from_parent, W_OK))
  122.     error (1, errno, "cannot write to `%s'", from_parent);
  123.   if (access (to_parent, W_OK))
  124.     error (1, errno, "cannot write to `%s'", to_parent);
  125.  
  126.   /* We can't make this atomic, but we do our best. */
  127.   for (i = NSIG; i > 0; i--)
  128.     if (i != SIGKILL)
  129.       signal (i, SIG_IGN);
  130.   setuid (0);            /* Make real uid 0 so it is harder to kill. */
  131.   nice (HIPRI - nice (0));    /* Raise priority. */
  132.   /* Make sure that `from' is not an ancestor of `to', to prevent
  133.      corruption of the file system in cases like
  134.      mv_dir foo foo/bar/baz
  135.      where foo and foo/bar are directories and foo/bar/baz does not exist. */
  136.   
  137.   next_slash = to;
  138.   while ((next_slash = index (next_slash, '/')) != NULL)
  139.     {
  140.       temp = *++next_slash;
  141.       *next_slash = '\0';
  142.       if (stat (to, &to_stats))
  143.     error (1, errno, "%s", to);
  144.       *next_slash = temp;
  145.       
  146.       if (to_stats.st_dev == from_stats.st_dev
  147.       && to_stats.st_ino == from_stats.st_ino)
  148.     error (1, 0, "`%s' is an ancestor of `%s'", from, to);
  149.     }
  150.  
  151.   if (link (from, to))
  152.     error (1, errno, "cannot link `%s' to `%s'", from, to);
  153.   if (unlink (from))
  154.     error (1, errno, "cannot unlink `%s'", from);
  155.  
  156.   /* Replace the directory's `..' entry.  It used to be a link to
  157.      the parent of `from'; make it a link to the parent of `to' instead. */
  158.   i = strlen (to);
  159.   next_slash = xmalloc (i + 4);
  160.   strcpy (next_slash, to);
  161.   strcpy (next_slash + i, "/..");
  162.   if (unlink (next_slash) && errno != ENOENT)
  163.     error (1, errno, "cannot unlink `%s'", next_slash);
  164.   if (link (to_parent, next_slash))
  165.     error (1, errno, "cannot link `%s' to `%s'", to_parent, next_slash);
  166.  
  167.   exit (0);
  168. }
  169.  
  170. /* Return NAME with any leading path stripped off.  */
  171.  
  172. char *
  173. basename (name)
  174.      char *name;
  175. {
  176.   char *base;
  177.  
  178.   base = rindex (name, '/');
  179.   return base ? base + 1 : name;
  180. }
  181.  
  182. /* Allocate `n' bytes of memory dynamically, with error checking.  */
  183.  
  184. char *
  185. xmalloc (n)
  186.      unsigned n;
  187. {
  188.   char *p;
  189.  
  190.   p = malloc (n);
  191.   if (p == 0)
  192.     error (1, 0, "virtual memory exhausted");
  193.   return p;
  194. }
  195.  
  196. /* Remove trailing slashes from STR;
  197.    they cause some system calls to fail. */
  198.  
  199. void
  200. strip_trailing_slashes (str)
  201.      char *str;
  202. {
  203.   int last = strlen (str) - 1;
  204.  
  205.   while (last > 0 && str[last] == '/')
  206.     str[last--] = '\0';
  207. }
  208.